/**
 * 放置Fiber的核心逻辑
 */

import { arrified, createStateNode, createTaskQueue, getRoot, getTag } from '../Misc';
import { updateNodeElement } from '../DOM'
const taskQueue = createTaskQueue()
// 要执行的子任务
let subTask = null
// 全局变量存储最外层的fiber
let pendingCommit = null

const commitAllWork = function (fiber) {
    // 初始化渲染
    fiber.effects.forEach(item => {
        if (item.tag === "class_component") {
            item.stateNode.__fiber = item
        }
        if (item.effectTag === "delete") {
            // 删除节点
            item.parent.stateNode.removeChild(item.stateNode)
        } else if (item.effectTag === "update") {
            // 更新节点
            if (item.type === item.alternate.type) {
                // 节点类型相同
                updateNodeElement(item.stateNode, item, item.alternate)
            } else {
                // 节点类型不同
                item.parent.stateNode.replaceChild(item.stateNode, item.alternate.stateNode)
            }
        } else if (item.effectTag === 'placement') {
            // 向页面中追加节点，当前要追加的子节点
            let fiber = item
            // 当前要追加的子节点的父级
            let parentFiber = item.parent
            // 找到普通节点父级 排除组件父级，因为组件父级是不能直接追加真实DOM节点的
            while (parentFiber.tag === "class_component" || parentFiber.tag === "function_component") {
                parentFiber = parentFiber.parent
            }
            // 如果子节点是普通节点 找到父级 将子节点追加到父级中
            if (fiber.tag === "host_component") {
                parentFiber.stateNode.appendChild(fiber.stateNode)
            }
        }
    })
    // 备份旧的fiber节点对象
    fiber.stateNode.__rootFiberContainer = fiber
}

const getFirstTask = () => {
    // 从任务队列中获取任务
    const task = taskQueue.pop()

    // 判断是否是组件状态更新任务
    if(task.from === 'class_component') {
        const root = getRoot(task.instance)
        task.instance.__fiber.partialState = task.partialState

        return {
            props: root.props,
            stateNode: root.stateNode,
            tag: "host_root",
            effects: [],
            child: null,
            alternate: root
        }
    }
    // 返回最外层节点的标记对象
    return {
        props: task.props,
        stateNode: task.dom,
        tag: "host_root",
        effects: [],
        child: null,
        alternate: task.dom.__rootFiberContainer
    }

}
const reconcileChildren = (fiber, children) => {

    // children 有可能是对象或数组
    // 将children转换成数组
    const arrifiedChildren = arrified(children)

    let index = 0,
        numberOfElements = arrifiedChildren.length,
        element = null,
        newFiber = null,
        prevFiber = null,
        alternate = null;

    if (fiber.alternate && fiber.alternate.child) {
        // 存在备份节点,
        alternate = fiber.alternate.child
    }

    while (index < numberOfElements || alternate) {
        // 子级 virtualDOM 对象
        element = arrifiedChildren[index]

        if (!element && alternate) {
            // 删除操作
            alternate.effectTag = "delete"
            fiber.effects.push(alternate)
        } else if (element && alternate) {
            // 更新操作
            newFiber = {
                type: element.type,
                props: element.props,
                tag: getTag(element),
                effects: [],
                effectTag: "update",
                parent: fiber,
                alternate
            }
            if (element.type === alternate.type) {
                // 类型相同
                newFiber.stateNode = alternate.stateNode
            } else {
                // 类型不同
                newFiber.stateNode = createStateNode(newFiber)
            }
        } else if (element && !alternate) {
            // 初始渲染
            newFiber = {
                type: element.type,
                props: element.props,
                tag: getTag(element),
                effects: [],
                effectTag: "placement",
                parent: fiber
            }

            // 设置stateNode
            newFiber.stateNode = createStateNode(newFiber)

        }


        // 构建节点的顺序
        if (index == 0) {
            // 第一个是子节点
            fiber.child = newFiber
        } else if (element) {
            // 其他的就是第一个的兄弟节点
            prevFiber.sibling = newFiber
        }

        if (alternate && alternate.sibling) {
            alternate = alternate.sibling
        } else {
            alternate = null
        }

        // 将prevFiber设置为第一个子节点
        prevFiber = newFiber

        index++
    }
}
// 构建子级fiber对象
const executeTask = fiber => {
    // 判断是否是class组件
    if (fiber.tag === 'class_component') {
        if(fiber.stateNode.__fiber && fiber.stateNode.__fiber.partialState) {
            fiber.stateNode.state = {
                ...fiber.stateNode.state,
                ...fiber.stateNode.__fiber.partialState
            }
        }
        reconcileChildren(fiber, fiber.stateNode.render())
    } else if (fiber.tag === "function_component") {
        reconcileChildren(fiber, fiber.stateNode(fiber.props))
    } else {
        reconcileChildren(fiber, fiber.props.children)
    }
    // 有子级返回子级
    if (fiber.child) {
        return fiber.child
    }

    let currentExecutelyFiber = fiber
    // 没有子级查看有没有兄弟节点
    while (currentExecutelyFiber.parent) {
        // 将所有 Fiber 对象都存储在最外层对象的 effects 中，其他节点的 effects 数组负责收集 Filber 对象
        currentExecutelyFiber.parent.effects = currentExecutelyFiber.parent.effects.concat(
            currentExecutelyFiber.effects.concat([currentExecutelyFiber])
        )
        // 如果有兄弟节点就构建兄弟节点
        if (currentExecutelyFiber.sibling) {
            return currentExecutelyFiber.sibling
        }
        // 没有就后退到父级节点
        currentExecutelyFiber = currentExecutelyFiber.parent
    }
    pendingCommit = currentExecutelyFiber
}

const workLoop = deadline => {
    // 获取任务
    if (!subTask) {
        subTask = getFirstTask()
    }
    // 如果有空余任务且浏览器空余时间大于1毫秒
    while (subTask && deadline.timeRemaining() > 1) {
        // executeTask返回新任务用以继续执行
        subTask = executeTask(subTask)
    }
    // 判断pendingCommit是否存在
    if (pendingCommit) {
        // 存在就是说明所有的遍历都走完了
        commitAllWork(pendingCommit)
    }
}

const performTask = deadline => {
    // 执行任务
    workLoop(deadline)
    // 如果有任务,还需要继续调用
    if (subTask || !taskQueue.isEmpty()) {
        requestIdleCallback(performTask)
    }
}

export const render = (element, dom) => {
    /**
     * 1. 向任务队列添加任务
     * 2. 指定在浏览器空闲时执行任务
     */

    /**
     * 任务就是通过 vdom 对象构建 Fiber 对象
     */
    taskQueue.push({
        dom,
        props: { children: element }
    })

    // 利用requestIdleCallback()执行任务
    requestIdleCallback(performTask)

}

export const scheduleUpdate = (instance, partialState) => {
    taskQueue.push({
      from: "class_component",
      instance,
      partialState
    })
    requestIdleCallback(performTask)
  }